﻿#pragma once

#include <memory>
#include <string>
#include <iostream>
#include <unordered_map>
#include <atomic>
#include <spdlog/spdlog.h>
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/common.h"
#include "spdlog/formatter.h"
#include "spdlog/sinks/sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "singleton.hpp"
#include "baselogger.hpp"



/*************************************************/
/****************** 通用日志定义 ******************/
/*************************************************/
namespace level {
    enum logger_level {
        trace   = spdlog::level::trace,
        debug   = spdlog::level::debug,
        info    = spdlog::level::info,
        warn    = spdlog::level::warn,
        err     = spdlog::level::err,
        critical= spdlog::level::critical,
        off     = spdlog::level::off
    };
}

#define     LOGGER_TFUNC_DISABLE        0                              
#define     LOGGER_TFUNC_ENABLE         1               
#define     LOGGER_TFUNC_FORMAT         "[{}=>{}()=>{}] "                
#define     LOGGER_TFMT_PATTERN         "[%D %T.%e] [%6P:%6t] [%8n] [%^%7l%$] %v"
#define     LOGGER_FILE_SIZE            (20<<20) 
#define     LOGGER_FILE_RNUMS           50
#define     LOGGER_SYSNAME              "SYSTEM"

/*************************************************/
/**************** spdlog日志实现 ******************/
/*************************************************/

class Spdlogger:public BaseLogger
{
    public:
        bool init(const std::string& fullPath){
            if (m_is_init_)
                return true;
            else
                m_is_init_=true;
                    
            try {
                    m_sinks_.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
                    m_sinks_.push_back(std::make_shared<spdlog::sinks::rotating_file_sink_mt>(fullPath, m_file_size_, m_file_rotanums_));
                    m_logger_unmap_.emplace(LOGGER_SYSNAME, std::make_shared<spdlog::logger>(LOGGER_SYSNAME, begin(m_sinks_), end(m_sinks_)));
                    spdlog::set_pattern(LOGGER_TFMT_PATTERN);
                    spdlog::set_level(spdlog::level::trace);
    
                    auto it = m_logger_unmap_.find(LOGGER_SYSNAME);
                    if ( it != m_logger_unmap_.end()) 
                        spdlog::register_logger(it->second);
                        it->second->set_pattern(LOGGER_TFMT_PATTERN);
                        it->second->flush_on(spdlog::level::trace);
                    
            }
            catch (const spdlog::spdlog_ex& ex) {
                std::cout << "log initialization failed: " << ex.what() << "file=" << __FUNCTION__ << ",line=" << __LINE__ << std::endl;
                return false;
            }
            return true;
        };     

        void uninit(){
            spdlog::flush_on(spdlog::level::trace);
            spdlog::drop_all();
        };

        inline const std::shared_ptr<spdlog::logger> get_logger(const std::string &moduleName) {
            auto it = m_logger_unmap_.find(moduleName);
            if ( it == m_logger_unmap_.end()) {
                return m_logger_unmap_.find(LOGGER_SYSNAME)->second;
            }
            return it->second;
        };
        void logtype_set_level(const std::string &moduleName, const spdlog::level::level_enum& log_level){

            try {
                auto it = m_logger_unmap_.find(moduleName);
                if ( it != m_logger_unmap_.end() ) {
                    it->second->set_level((spdlog::level::level_enum)log_level);
                }
            }
            catch (const spdlog::spdlog_ex& ex) {
                std::cout << "log set level failed: " << ex.what() << "file=" << __FUNCTION__ << ",line=" << __LINE__ << std::endl;
            }
            return;
        };
        bool logger_register(const std::string &module_name){
            auto it = m_logger_unmap_.find(module_name);
            if ( it != m_logger_unmap_.end() ) {
                return false;
            }
            try {
                std::shared_ptr<spdlog::logger>  module_logger = std::make_shared<spdlog::logger>(module_name, begin(m_sinks_), end(m_sinks_));
                m_logger_unmap_.emplace(module_name, module_logger);
                spdlog::register_logger(module_logger);
                module_logger->set_pattern(LOGGER_TFMT_PATTERN);
                module_logger->flush_on(spdlog::level::trace);
            }
            catch (const spdlog::spdlog_ex& ex) {
                std::cout << "log register failed: " << ex.what() << "file=" << __FUNCTION__ << ",line=" << __LINE__ << std::endl;
                return false;
            }
            return true;
        };
        bool logger_option(const std::string& moduleName, std::uint32_t moduleLevel, bool funcIsTrace){
            (void)funcIsTrace;
            logtype_set_level(moduleName, (spdlog::level::level_enum)moduleLevel);
            return true;
        };
        void logger_print(const std::string& moduleName, std::uint32_t level, const std::string& msg)
        {
            switch (level)
            {
                case TRACE:
                    get_logger(moduleName)->trace(msg);
                    break;
                case DEBUG:
                    get_logger(moduleName)->debug(msg);
                    break;
                case INFO:
                    get_logger(moduleName)->info(msg);
                    break;
                case WARN:
                    get_logger(moduleName)->warn(msg);
                    break;
                case ERR:
                    get_logger(moduleName)->error(msg);
                    break;
                case CRITICAL:
                    get_logger(moduleName)->critical(msg);
                    break;
                default:
                break;
            }
        }
    private:
        std::vector<spdlog::sink_ptr>                                           m_sinks_;
        std::unordered_map<std::string, std::shared_ptr<spdlog::logger>>        m_logger_unmap_;
        uint32_t                                                                m_file_size_;
        uint32_t                                                                m_file_rotanums_;
        bool                                                                    m_is_init_;

    public:
        Spdlogger():
            m_file_size_(LOGGER_FILE_SIZE),
            m_file_rotanums_(LOGGER_FILE_RNUMS),
            m_is_init_(false){};
        ~Spdlogger(){};
        Spdlogger(const Spdlogger&) = delete;
        Spdlogger& operator=(const Spdlogger&) = delete;
};
typedef std::shared_ptr<Spdlogger>              spdlogger_sptr;

/*************************************************/
/******************  模块宏定义  *****************/
/*************************************************/
#define DECLARE_MODULE_LOGGER(module_label) \
class mLogger_##module_label\
{\
    public:\
        bool logger_init(base_logger_sptr loggerSptr, const std::string& moduleName) {\
            if ( nullptr == loggerSptr ) \
                    return false;\
            m_mlogger_ptr_       = loggerSptr;\
            m_module_name_str_   = moduleName;\
            loggerSptr->logger_register(moduleName);\
            return true;\
        }\
        void log_set_option(const std::string& moduleName, std::uint32_t  moduleLevel, std::uint32_t funcIsTrace) {\
            m_mlogger_ptr_->logger_option(moduleName, moduleLevel, funcIsTrace);\
            m_module_level_= moduleLevel;\
            m_module_is_tfunc_ = funcIsTrace;\
        }\
        std::uint32_t       log_get_level() { return m_module_level_; }\
        base_logger_sptr    log_get_logger() const { return m_mlogger_ptr_; }\
        template<typename... Args>\
        inline void trace(const std::string& formatStr, const Args &... args)\
        {\
            m_mlogger_ptr_->logger_print(m_module_name_str_, level::logger_level::trace, fmt::format(formatStr, args...));\
        }\
        template<typename... Args>\
        inline void debug(const std::string& formatStr, const Args &... args)\
        {\
            m_mlogger_ptr_->logger_print(m_module_name_str_, level::logger_level::debug, fmt::format(formatStr, args...));\
        }\
        template<typename... Args>\
        inline void info(const std::string& formatStr, const Args &... args)\
        {\
            m_mlogger_ptr_->logger_print(m_module_name_str_, level::logger_level::info, fmt::format(formatStr, args...));\
        }\
        template<typename... Args>\
        inline void warn(const std::string& formatStr, const Args &... args)\
        {\
            m_mlogger_ptr_->logger_print(m_module_name_str_, level::logger_level::warn, fmt::format(formatStr, args...));\
        }\
        template<typename... Args>\
        inline void error(const std::string& formatStr, const Args &... args)\
        {\
            m_mlogger_ptr_->logger_print(m_module_name_str_, level::logger_level::err, fmt::format(formatStr, args...));\
        }\
        template<typename... Args>\
        inline void critical(const std::string& formatStr, const Args &... args)\
        {\
            m_mlogger_ptr_->logger_print(m_module_name_str_, level::logger_level::critical, fmt::format(formatStr, args...));\
        }\
        static mLogger_##module_label& GetInstance()\
        {\
            static mLogger_##module_label  s_instance;\
            return s_instance;\
        }\
        mLogger_##module_label():\
            m_mlogger_ptr_(nullptr),\
            m_module_name_str_(""){}\
        ~mLogger_##module_label() {}\
    private:\
        base_logger_sptr                    m_mlogger_ptr_      = nullptr;\
        std::string                         m_module_name_str_  = "";\
    public:\
        std::uint32_t                       m_module_level_      = level::logger_level::info;\
        std::atomic_bool                    m_module_is_tfunc_{false};\
};

#define LOGGER_INIT(module_label, module_name, base_logger) \
do{mLogger_##module_label::GetInstance().logger_init(base_logger, module_name);}while(0);

#define LOGGER_SET_LEVEL(module_label, module_name, level, isTraceFunc)\
do{mLogger_##module_label::GetInstance().log_set_option(module_name, level, isTraceFunc);}while(0);

#define LOGGER_CRITICAL(module_label, msgfmt, ...) \
do{ \
if ( mLogger_##module_label::GetInstance().m_module_level_ > level::logger_level::critical) break;\
if ( mLogger_##module_label::GetInstance().m_module_is_tfunc_ )\
{std::string fmStr=LOGGER_TFUNC_FORMAT;mLogger_##module_label::GetInstance().critical(fmStr+msgfmt, SPDLOG_FILE_BASENAME(__FILE__), __FUNCTION__, __LINE__, ##__VA_ARGS__);}else\
{mLogger_##module_label::GetInstance().critical(msgfmt, ##__VA_ARGS__);}\
}while(0);


#define LOGGER_ERR(module_label, msgfmt, ...) \
do{ \
if ( mLogger_##module_label::GetInstance().m_module_level_ > level::logger_level::err) break;\
if ( mLogger_##module_label::GetInstance().m_module_is_tfunc_ )\
{std::string fmStr=LOGGER_TFUNC_FORMAT;mLogger_##module_label::GetInstance().error(fmStr+msgfmt, SPDLOG_FILE_BASENAME(__FILE__), __FUNCTION__, __LINE__, ##__VA_ARGS__);}else\
{mLogger_##module_label::GetInstance().error(msgfmt, ##__VA_ARGS__);}\
}while(0);



#define LOGGER_WARN(module_label, msgfmt, ...) \
do{ \
if ( mLogger_##module_label::GetInstance().m_module_level_ > level::logger_level::warn) break;\
if ( mLogger_##module_label::GetInstance().m_module_is_tfunc_ ) \
{std::string fmStr=LOGGER_TFUNC_FORMAT;mLogger_##module_label::GetInstance().warn(fmStr+msgfmt, SPDLOG_FILE_BASENAME(__FILE__), __FUNCTION__, __LINE__, ##__VA_ARGS__);}else\
{mLogger_##module_label::GetInstance().warn(msgfmt, ##__VA_ARGS__);}\
}while(0);


#define LOGGER_INFO(module_label, msgfmt, ...) \
do{ \
if ( mLogger_##module_label::GetInstance().m_module_level_ > level::logger_level::info) break;\
if ( mLogger_##module_label::GetInstance().m_module_is_tfunc_ ) \
{std::string fmStr=LOGGER_TFUNC_FORMAT;mLogger_##module_label::GetInstance().info(fmStr+msgfmt, SPDLOG_FILE_BASENAME(__FILE__), __FUNCTION__, __LINE__, ##__VA_ARGS__);}else\
{mLogger_##module_label::GetInstance().info(msgfmt, ##__VA_ARGS__);}\
}while(0);


#define LOGGER_DEBUG(module_label, msgfmt, ...) \
do{ \
if ( mLogger_##module_label::GetInstance().m_module_level_ > level::logger_level::debug) break;\
if ( mLogger_##module_label::GetInstance().m_module_is_tfunc_ ) \
{std::string fmStr=LOGGER_TFUNC_FORMAT;mLogger_##module_label::GetInstance().debug(fmStr+msgfmt, SPDLOG_FILE_BASENAME(__FILE__), __FUNCTION__, __LINE__, ##__VA_ARGS__);}else\
{mLogger_##module_label::GetInstance().debug(msgfmt, ##__VA_ARGS__);}\
}while(0);



#define LOGGER_TRACE(module_label, msgfmt, ...) \
do{ \
if ( mLogger_##module_label::GetInstance().m_module_level_ > level::logger_level::trace) break;\
if ( mLogger_##module_label::GetInstance().m_module_is_tfunc_ ) \
{std::string fmStr=LOGGER_TFUNC_FORMAT;mLogger_##module_label::GetInstance().trace(fmStr+msgfmt, SPDLOG_FILE_BASENAME(__FILE__), __FUNCTION__, __LINE__, ##__VA_ARGS__);}else\
{mLogger_##module_label::GetInstance().trace(msgfmt, ##__VA_ARGS__);}\
}while(0);

